package org.jspresso.tester.utils;

import java.util.Collection;
import java.util.Iterator;

import org.jspresso.framework.action.ActionBusinessException;
import org.jspresso.framework.application.backend.BackendControllerHolder;
import org.jspresso.framework.application.backend.IBackendController;
import org.jspresso.framework.model.component.IComponent;
import org.jspresso.framework.model.descriptor.ICollectionPropertyDescriptor;
import org.jspresso.framework.model.descriptor.IComponentDescriptor;
import org.jspresso.framework.model.descriptor.IPropertyDescriptor;
import org.jspresso.framework.model.descriptor.IReferencePropertyDescriptor;
import org.jspresso.framework.model.descriptor.IScalarPropertyDescriptor;
import org.jspresso.framework.model.descriptor.basic.BasicListDescriptor;
import org.jspresso.framework.util.accessor.IAccessor;
import org.jspresso.framework.util.accessor.ICollectionAccessor;

public class EntityComparator {

	public static boolean isEquals(IComponent firstComponent, IComponent secondComponent, Collection<IPropertyDescriptor> excludedproperties) {
		if(firstComponent == null && secondComponent == null) {
			return true;
		}
		else if(firstComponent == null || secondComponent == null) {
			return false;
		}
		else if(firstComponent.getComponentContract() != secondComponent.getComponentContract()) {
			return false;
		}
		else if(firstComponent.equals(secondComponent)){ // Is exactly the same
			return true;
		}
		else { // Need to recursively compare every properties

			boolean componentEquals = true;

			IBackendController controller = BackendControllerHolder.getCurrentBackendController();
			IComponentDescriptor<?> descriptor = controller.getEntityFactory().getComponentDescriptor(firstComponent.getComponentContract());
			for(IPropertyDescriptor propertyDescriptor : descriptor.getPropertyDescriptors()) {
				if(!propertyDescriptor.isComputed() && !excludedproperties.contains(propertyDescriptor)) {

					if(propertyDescriptor instanceof IScalarPropertyDescriptor) {
						if(!firstComponent.straightGetProperty(propertyDescriptor.getName()).equals(secondComponent.straightGetProperty(propertyDescriptor.getName()))) {
							componentEquals = false;
							break;
						}
					}
					else if(propertyDescriptor instanceof IReferencePropertyDescriptor) {
						IAccessor accessor = controller.getEntityFactory().getAccessorFactory().createPropertyAccessor(propertyDescriptor.getName(), descriptor.getComponentContract());

						try {
							IComponent firstComponentPropertyValue = accessor.getValue(firstComponent);
							IComponent secondComponentPropertyValue = accessor.getValue(secondComponent);

							if(!isEquals(firstComponentPropertyValue, secondComponentPropertyValue, excludedproperties)) {
								componentEquals = false;
								break;							
							}

						} catch (Exception e) {
							e.printStackTrace();
							throw new ActionBusinessException(e.getMessage());
						}
					}
					else if(propertyDescriptor instanceof ICollectionPropertyDescriptor) {

						ICollectionAccessor accessor = controller.getEntityFactory().getAccessorFactory().createCollectionPropertyAccessor(propertyDescriptor.getName(), descriptor.getComponentContract(), ((ICollectionPropertyDescriptor<?>) propertyDescriptor).getReferencedDescriptor().getElementDescriptor().getComponentContract());

						try {
							Collection<IComponent> col1 = accessor.getValue(firstComponent);
							Collection<IComponent> col2 = accessor.getValue(secondComponent);
							
							if(col1.size() != col2.size()) {
								componentEquals = false;
								break;
							}
							Iterator<IComponent> it1 = col1.iterator();
							Iterator<IComponent> it2 = col2.iterator();

							if(((ICollectionPropertyDescriptor<?>)propertyDescriptor).getCollectionDescriptor() instanceof BasicListDescriptor<?>) {
								while(it1.hasNext() && it2.hasNext()) {
									IComponent component1 = it1.next();
									IComponent component2 = it2.next();
									if(!isEquals(component1, component2, excludedproperties)) {
										componentEquals = false;
										break;
									}
								}
								if(!componentEquals) {
									break;
								}
							}
							else {
								while(it1.hasNext()) {
									IComponent component1 = it1.next();
									boolean found = false;
									it2 = col2.iterator();
									while(it2.hasNext()) {
										IComponent component2 = it2.next();
										if(isEquals(component1, component2, excludedproperties)) {
											found = true;
											break;
										}
									}
									if(!found) {
										componentEquals = false;
										break;
									}
								}
							}

						}
						catch(Exception e) {
							e.printStackTrace();
							throw new ActionBusinessException(e.getMessage());
						}
					}
				}
			}
			return componentEquals;
		}
	}

}
